import os
import json
import tkinter as tk
from tkinter import filedialog, messagebox, Listbox, Scrollbar, END
from datetime import datetime
import locale
import ttkbootstrap as ttk
from ttkbootstrap.constants import *



CONFIG_FILE = "config.json"

def ms_to_srt_time(ms):
    hours = ms // 3600000
    minutes = (ms % 3600000) // 60000
    seconds = (ms % 60000) // 1000
    milliseconds = ms % 1000
    return f"{hours:02}:{minutes:02}:{seconds:02},{milliseconds:03}"

def convert_json_to_srt(filepath, srt_path):
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            data = json.load(f)

        captions = data["timelineWidget"]["timeline"]["captionTracks"][0]["captions"]

        with open(srt_path, "w", encoding="utf-8") as srt_file:
            for index, caption in enumerate(captions, start=1):
                in_ms = caption["inPoint"]
                out_ms = caption["outPoint"]
                text = caption["captionText"]

                srt_file.write(f"{index}\n")
                srt_file.write(f"{ms_to_srt_time(in_ms)} --> {ms_to_srt_time(out_ms)}\n")
                srt_file.write(f"{text}\n\n")

        return True, srt_path
    except Exception as e:
        return False, str(e)


def save_config(open_path=None, export_path=None):
    config = {}
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r") as f:
                config = json.load(f)
        except Exception:
            pass

    if open_path is not None:
        config["last_open_folder"] = open_path
    if export_path is not None:
        config["last_export_folder"] = export_path

    with open(CONFIG_FILE, "w") as f:
        json.dump(config, f)


def load_last_open_folder():
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r") as f:
                data = json.load(f)
            if "last_open_folder" in data and os.path.isdir(data["last_open_folder"]):
                return data["last_open_folder"]
        except Exception:
            pass
    return None

def load_last_export_folder():
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r") as f:
                data = json.load(f)
            if "last_export_folder" in data and os.path.isdir(data["last_export_folder"]):
                return data["last_export_folder"]
        except Exception:
            pass
    return None

from datetime import datetime
import re

def parse_recent_time(time_str):
    try:
        # 中文星期与月份的处理
        if re.search(r"[一二三四五六日]", time_str):
            # 移除“周一”或“星期一”等
            time_str = re.sub(r"(周|星期)[一二三四五六日]", "", time_str)
            # 将“6月”转换成英文缩写“Jun”
            time_str = re.sub(r"(\d{1,2})月", lambda m: datetime(2000, int(m.group(1)), 1).strftime("%b"), time_str)
            # 修复日期可能是单数字的问题，例如“1”变成“01”
            time_str = re.sub(r"\b(\d{1})\b(?= \d{2}:\d{2}:\d{2})", r"0\1", time_str)
            # 加上固定星期几（例如 "Sun"），否则无法使用 `%a`
            time_str = "Sun " + time_str.strip()
        # 尝试解析英文时间格式
        return datetime.strptime(time_str.strip(), "%a %b %d %H:%M:%S %Y")
    except Exception as e:
        return None



def refresh_file_list(folder):
    file_list.delete(0, END)
    file_list.config(state=tk.NORMAL)
    path_label.config(text=folder)

    app.file_map = {}
    items = []

    for filename in os.listdir(folder):
        if filename.lower().endswith((".json", ".bjson")):
            filepath = os.path.join(folder, filename)
            src_path = None
            used_time = ""
            used_dt = datetime.min  # 默认最旧

            try:
                with open(filepath, "r", encoding="utf-8") as f:
                    data = json.load(f)

                # 优先从 mainWindow->browserPanelFiles 获取
                panel_files = data.get("mainWindow", {}).get("browserPanelFiles", [])
                if isinstance(panel_files, list) and len(panel_files) > 0:
                    file_obj = panel_files[0]
                    src_path = file_obj.get("srcPath", "").strip()
                    used_time = file_obj.get("recentlyUsedTime", "")
                    dt = parse_recent_time(used_time)
                    if dt:
                        used_dt = dt

                # 补充 timelineWidget 情况（若无 srcPath）
                if not src_path:
                    captions = data.get("timelineWidget", {}).get("timeline", {}).get("captionTracks", [])
                    if captions and isinstance(captions, list):
                        src_path = captions[0].get("srcPath", "").strip()

                if src_path:
                    display_text = f"{src_path}"
                    if used_time and used_dt != datetime.min:
                        display_text += f" （{used_dt.strftime('%Y-%m-%d %H:%M:%S')}）"
                    items.append((used_dt, display_text, filepath))
                else:
                    # 无效结构
                    items.append((datetime.min, f"[无 srcPath] {filename}", filepath))
            except Exception:
                items.append((datetime.min, f"[格式错误] {filename}", filepath))

    # 按时间从最近到最远排序
    for _, display_name, filepath in sorted(items, key=lambda x: x[0], reverse=True):
        app.file_map[display_name] = filepath
        file_list.insert(END, display_name)

    if not app.file_map:
        file_list.insert(END, "（当前目录无有效 .json 或 .bjson 文件）")
        file_list.config(state=tk.DISABLED)



def open_folder():
    folder = filedialog.askdirectory()
    if folder:
        app.folder_path = folder
        save_config(open_path=folder)
        refresh_file_list(folder)


def generate_srt():
    selected = file_list.curselection()
    if not selected:
        messagebox.showwarning("未选择", "请选择一个文件")
        return

    display_name = file_list.get(selected[0])
    filepath = app.file_map.get(display_name)
    if not filepath:
        messagebox.showerror("错误", "无法获取对应的文件路径")
        return

    # 弹出“另存为”对话框
    # 尝试从 display_name 中提取 srcPath 的文件名部分
    src_path = display_name.split(" （")[0].strip()  # 去掉后面的时间
    default_filename = os.path.splitext(os.path.basename(src_path))[0] + ".srt"

    srt_path = filedialog.asksaveasfilename(
        title="保存 SRT 文件",
        defaultextension=".srt",
        initialfile=default_filename,
        filetypes=[("SRT 字幕文件", "*.srt"), ("所有文件", "*.*")]
    )

    if not srt_path:
        return  # 用户取消保存

    success, message = convert_json_to_srt(filepath, srt_path)
    if success:
        messagebox.showinfo("成功", f"SRT 文件已保存到:\n{message}")
        save_config(export_path=os.path.dirname(srt_path))  # 在 generate_srt 中

    else:
        messagebox.showerror("错误", f"转换失败:\n{message}")

def convert_json_to_txt(json_path, txt_path):
    try:
        with open(json_path, "r", encoding="utf-8") as f:
            data = json.load(f)

        captions = data["timelineWidget"]["timeline"]["captionTracks"][0]["captions"]

        with open(txt_path, "w", encoding="utf-8") as txt_file:
            for caption in captions:
                text = caption["captionText"]
                txt_file.write(text + "\n")
        return True, txt_path
    except Exception as e:
        return False, str(e)



def generate_txt():
    selected = file_list.curselection()
    if not selected:
        messagebox.showwarning("未选择", "请选择一个文件")
        return

    display_name = file_list.get(selected[0])
    filepath = app.file_map.get(display_name)
    if not filepath:
        messagebox.showerror("错误", "未找到对应文件路径")
        return

    # 取 srcPath 名字生成默认文件名
    src_path = display_name.split(" （")[0].strip()
    default_filename = os.path.splitext(os.path.basename(src_path))[0] + ".txt"

    initial_dir = load_last_export_folder() or app.folder_path or os.getcwd()


    txt_path = filedialog.asksaveasfilename(
        title="保存 TXT 文件",
        defaultextension=".txt",
        initialfile=default_filename,
        initialdir=initial_dir,
        filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
    )

    if txt_path:
        success, message = convert_json_to_txt(filepath, txt_path)
        if success:
            messagebox.showinfo("成功", f"TXT 文件已生成:\n{message}")
            # 记忆保存路径
            save_config(export_path=os.path.dirname(txt_path))
        else:
            messagebox.showerror("错误", f"转换失败:\n{message}")


def ms_to_ass_time(ms):
    # ASS时间格式：H:MM:SS.cs （cs为百分之一秒）
    hours = ms // 3600000
    minutes = (ms % 3600000) // 60000
    seconds = (ms % 60000) // 1000
    centiseconds = (ms % 1000) // 10
    return f"{hours}:{minutes:02}:{seconds:02}.{centiseconds:02}"

def convert_json_to_ass(json_path, ass_path):
    try:
        with open(json_path, "r", encoding="utf-8") as f:
            data = json.load(f)

        captions = data["timelineWidget"]["timeline"]["captionTracks"][0]["captions"]

        with open(ass_path, "w", encoding="utf-8") as ass_file:
            # 写ASS文件头部（最基础，简单版本）
            ass_file.write("[Script Info]\n")
            ass_file.write("ScriptType: v4.00+\n")
            ass_file.write("Collisions: Normal\n")
            ass_file.write("PlayDepth: 0\n")
            ass_file.write("Timer: 100.0000\n")
            ass_file.write("\n[V4+ Styles]\n")
            ass_file.write("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
                           "Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, "
                           "Alignment, MarginL, MarginR, MarginV, Encoding\n")
            ass_file.write("Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H64000000,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,1\n")
            ass_file.write("\n[Events]\n")
            ass_file.write("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")

            # 写字幕条目
            for caption in captions:
                start = ms_to_ass_time(caption["inPoint"])
                end = ms_to_ass_time(caption["outPoint"])
                text = caption["captionText"].replace("\n", "\\N")  # ASS换行符是 \N

                ass_file.write(f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}\n")

        return True, ass_path
    except Exception as e:
        return False, str(e)

def generate_ass():
    selected = file_list.curselection()
    if not selected:
        messagebox.showwarning("未选择", "请选择一个文件")
        return

    display_name = file_list.get(selected[0])
    filepath = app.file_map.get(display_name)
    if not filepath:
        messagebox.showerror("错误", "未找到对应文件路径")
        return

    src_path = display_name.split(" （")[0].strip()
    default_filename = os.path.splitext(os.path.basename(src_path))[0] + ".ass"

    initial_dir = load_last_export_folder() or app.folder_path or os.getcwd()


    ass_path = filedialog.asksaveasfilename(
        title="保存 ASS 文件",
        defaultextension=".ass",
        initialfile=default_filename,
        initialdir=initial_dir,
        filetypes=[("ASS 字幕文件", "*.ass"), ("所有文件", "*.*")]
    )

    if ass_path:
        success, message = convert_json_to_ass(filepath, ass_path)
        if success:
            messagebox.showinfo("成功", f"ASS 文件已生成:\n{message}")
            save_config(export_path=os.path.dirname(ass_path))
        else:
            messagebox.showerror("错误", f"转换失败:\n{message}")

# 创建主界面
app = tk.Tk()
app.title("必剪字幕导出工具")
app.geometry("600x450")
app.folder_path = ""
app.file_map = {}

# 顶部路径选择栏
top_frame = tk.Frame(app)
top_frame.pack(pady=10, padx=10, fill=tk.X)

btn_select = tk.Button(top_frame, text="选择路径", command=open_folder, height=2, width=12)
btn_select.pack(side=tk.LEFT)

path_label = tk.Label(top_frame, text="", anchor="w", wraplength=500)
path_label.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True)

# 列表框 + 滚动条
frame = tk.Frame(app)
frame.pack(fill=tk.BOTH, expand=True, padx=10)

scrollbar = Scrollbar(frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

file_list = Listbox(frame, selectmode=tk.SINGLE, yscrollcommand=scrollbar.set)
file_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=file_list.yview)


# 创建一个水平容器框架
btn_frame = tk.Frame(app)
btn_frame.pack(pady=10)

# 在容器里放生成 SRT 按钮
btn_generate = tk.Button(btn_frame, text="生成 SRT", command=generate_srt, height=2)
btn_generate.pack(side=tk.LEFT, padx=(0, 30))  # 右边加点间距

# 在容器里放生成 TXT 按钮（放在右边）
btn_generate_txt = tk.Button(btn_frame, text="生成 TXT", command=generate_txt, height=2)
btn_generate_txt.pack(side=tk.LEFT,padx=(0, 30))

btn_generate_ass = tk.Button(btn_frame, text="生成 ASS", command=generate_ass, height=2)
btn_generate_ass.pack(side=tk.LEFT, padx=(0, 30))

# 启动时尝试加载上次路径
last_folder = load_last_open_folder()
if last_folder:
    app.folder_path = last_folder
    refresh_file_list(last_folder)

# 主循环
app.mainloop()
